package com.ddxz.tool.web.component;

import com.ddxz.tool.core.convert.StringUtil;
import com.ddxz.tool.web.annotation.DdxzRequestParam;
import com.ddxz.tool.web.dto.DdxzException;
import com.ddxz.tool.web.dto.ErrorStatus;
import com.ddxz.tool.web.dto.PageRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 朱宗刚
 * @version 1.0.0
 * @description com.casic.common.tools.component.CasicRequestParamMethodArgumentResolver
 * @date 2020/4/9
 */
@Slf4j
public class DdxzRequestParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
    /**
     * Whether the given {@linkplain MethodParameter method parameter} is
     * supported by this resolver.
     *
     * @param parameter the method parameter to check
     * @return {@code true} if this resolver supports the supplied parameter;
     * {@code false} otherwise
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return (parameter.hasParameterAnnotation(DdxzRequestParam.class) &&
                PageRequest.class.isAssignableFrom(parameter.getParameterType()));
    }

    /**
     * Resolves a method parameter into an argument value from a given request.
     * A {@link ModelAndViewContainer} provides access to the model for the
     * request. A {@link WebDataBinderFactory} provides a way to create
     * type conversion purposes.
     *
     * @param parameter     the method parameter to resolve. This parameter must
     *                      have previously been passed to {@link #supportsParameter} which must
     *                      have returned {@code true}.
     * @param mavContainer  the ModelAndViewContainer for the current request
     * @param webRequest    the current request
     * @return the resolved argument value, or {@code null} if not resolvable
     * @throws Exception in case of errors with the preparation of argument values
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Class<?> clz = parameter.getParameterType();
        Class<?> cla;
        String setFuncName;
        String fieldName = null;
        Method method;
        try {
            Object rst = clz.newInstance();
            List<Field> fieldList = new ArrayList<>();
            do{
                fieldList.addAll(Arrays.asList(clz.getDeclaredFields()));
                cla = clz.getSuperclass();
                clz = cla;
            } while (!cla.equals(Object.class));

            clz = parameter.getParameterType();
            for (Field field : fieldList) {
                log.debug("{}", field.getClass().getName());
                fieldName = field.getName();
                setFuncName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                method = clz.getMethod(setFuncName, field.getType());
                if(webRequest.getParameter(field.getName()) != null) {
                    method.invoke(rst, StringUtil.getValue(webRequest.getParameter(field.getName()), field.getType()));
                }
            }

            log.debug("{}: {}", clz.getTypeName(), rst);
            return rst;
        } catch (InstantiationException e) {
            log.error(ErrorStatus.NO_DEFAULT_CONTRUCT.getMessage(), clz.getSimpleName(), e);
            throw new DdxzException(ErrorStatus.NO_DEFAULT_CONTRUCT, clz.getSimpleName());
        } catch (NoSuchMethodException e) {
            log.error(ErrorStatus.NO_SET_FUNCTION.getMessage(), fieldName, e);
            throw new DdxzException(ErrorStatus.NO_SET_FUNCTION, fieldName);
        }
    }
}
